//
// Created by RandBII on 2020/12/24.
//

#include "DecoderBase.h"


void DecoderBase::Start() {
    if (!m_Thread) {
        StartDecodingThread();
    } else {
        std::unique_lock<std::mutex> lock(m_Mutex);
        m_DecoderState = STATE_DECODING;
        m_Cond.notify_all();
    }
}

void DecoderBase::Pause() {
    std::unique_lock<std::mutex> lock(m_Mutex);
    m_DecoderState = STATE_PAUSE;
}

void DecoderBase::Stop() {
    std::unique_lock<std::mutex> lock(m_Mutex);
    m_DecoderState = STATE_PAUSE;
    m_Cond.notify_all();
}

float DecoderBase::GetDuration() {
    return m_Duration * 1.0f / 1000;
}

int DecoderBase::InitFFDecoder() {
    int result = -1;
    do {
        //1.创建封装格式上下文
        m_AVFormatContext = avformat_alloc_context();
//        2.打开文件
        if (avformat_open_input(&m_AVFormatContext, m_Url, NULL, NULL) != 0) {
            LOG_E("DecoderBase::InitFFDecoder()  avformat_open_input failed");
            break;
        }
//        3.获取音视频流信息
        if (avformat_find_stream_info(m_AVFormatContext, NULL) < 0) {
            LOG_E("DecoderBase::InitFFDecoder()  avformat_find_stream_info failed");
            break;
        }

//     4. 获取音视频流索引信息
        for (int i = 0; i < m_AVFormatContext->nb_streams; i++) {
            if (m_AVFormatContext->streams[i]->codecpar->codec_type == m_MediaType) {
                m_StreamIndex = i;
                break;
            }
        }
        if (m_StreamIndex == -1) {
            LOG_E("DecoderBase::InitFFDecoder()    failed to find steamIndex ");
            break;
        }
//    5.获取解码器参数
        AVCodecParameters *avCodecParameters = m_AVFormatContext->streams[m_StreamIndex]->codecpar;
//        6.获取解码器
        m_AVCodec = avcodec_find_decoder(avCodecParameters->codec_id);
        if (m_AVCodec == nullptr) {
            LOG_E("DecoderBase::InitFFDecoder()  failed to find  AVCodec  ");
            break;
        }
//        7.创建解码器上下文
        m_AVCodecContext = avcodec_alloc_context3(m_AVCodec);
        if (avcodec_parameters_to_context(m_AVCodecContext, avCodecParameters) < 0) {
            LOG_E("DecoderBase::InitFFDecoder()  avcodec_parameters_to_context  failed  ");
            break;
        }
//        8.打开解码器

        result = avcodec_open2(m_AVCodecContext, m_AVCodec, NULL);
        if (result < 0) {
            LOG_E("DecoderBase::InitFFDecoder()  avcodec_open2  failed  ");
            break;
        }

        result = 0;
//        单位毫秒
        m_Duration = m_AVFormatContext->duration / AV_TIME_BASE * 1000;
//        创建AVPacket 存放编码数据
        m_AVPacket = av_packet_alloc();
//    创建AVFrame 存放解码后数据
        m_AVFrame = av_frame_alloc();

    } while (false);

    if (result != 0 && m_MsgContext && m_MessageCallback) {
        m_MessageCallback(m_MsgContext, MSG_DECODER_INIT_ERROR, 0);
    }

    return result;
}

void DecoderBase::UnInitFFDecoder() {

    if (!m_AVFrame) { // 帧数据
        av_frame_free(&m_AVFrame);
        m_AVFrame = nullptr;
    }
    if (!m_AVPacket) {// 包数据
        av_packet_free(&m_AVPacket);
        m_AVPacket = nullptr;
    }
    if (!m_AVCodecContext) {// 编解码环境
        avcodec_free_context(&m_AVCodecContext);
        m_AVCodecContext = nullptr;
    }
    if (!m_AVFormatContext) {// 封装格式环境
        avformat_free_context(m_AVFormatContext);
        m_AVFormatContext = nullptr;
    }

}

void DecoderBase::StartDecodingThread() {
    m_Thread = new thread(DoAVDecoding, this);
}

void DecoderBase::DecodingLoop() {
    LOG_E("DecoderBase::DecodingLoop Looping");
    {
        std::unique_lock<std::mutex> lock(m_Mutex);
        m_Cond.wait_for(lock, std::chrono::milliseconds(10));
        lock.unlock();
    }
    for (;;) {
        while (m_DecoderState == STATE_PAUSE) {
            std::unique_lock<std::mutex> lock(m_Mutex);
            m_Cond.wait_for(lock, std::chrono::milliseconds(10));
            m_StartTimeStamp = TimeHelper::GetSysCurrentTime() - m_CurrentTimeStamp;
        }
        if (m_DecoderState == STATE_PAUSE) {
            break;
        }
        if (m_StartTimeStamp == -1) m_StartTimeStamp = TimeHelper::GetSysCurrentTime();

        if (DecodeOnePacket() != 0) {
//            解码结束，暂停解码器
            std::unique_lock<std::mutex> lock(m_Mutex);
            m_DecoderState = STATE_PAUSE;
        }
    }
    LOG_I("DecoderBase::DecodingLoop loop end ");
}

void DecoderBase::UpdateTimeStamp() {

    std::unique_lock<std::mutex> lock(m_Mutex);
    if (m_AVFrame->pkt_dts != AV_NOPTS_VALUE) {
        m_CurrentTimeStamp = m_AVFrame->pkt_dts;
    } else if (m_AVFrame->pts != AV_NOPTS_VALUE) {
        m_CurrentTimeStamp = m_AVFrame->pts;
    } else {
        m_CurrentTimeStamp = 0;
    }

    m_CurrentTimeStamp = (int64_t) (
            (m_CurrentTimeStamp * av_q2d(m_AVFormatContext->streams[m_StreamIndex]->time_base)) *
            1000);

    if (m_SeekPosition > 0 && m_SeekSuccess) {
        m_StartTimeStamp = TimeHelper::GetSysCurrentTime() - m_CurrentTimeStamp;
        m_SeekPosition = 0;
        m_SeekSuccess = false;
    }


}

/**
 * 同步有三种方式
 * 1.系统时针同步
 * 2.向视频时钟同步
 * 3.向音频时钟同步
 * 大部分采用 向音频时钟同步。
 * @return
 */
long DecoderBase::AVSync() {

    long delay = 0;

    long elapsedTime = TimeHelper::GetSysCurrentTime() - m_StartTimeStamp;
    if (m_MsgContext && m_MessageCallback && m_MediaType == AVMEDIA_TYPE_AUDIO) {
        m_MessageCallback(m_MsgContext, MSG_DECODING_TIME, m_CurrentTimeStamp * 1.0f / 1000);
    }
//    与系统时间进行同步
    if (m_CurrentTimeStamp > elapsedTime) {
//       休眠时间
        auto sleepTime = static_cast<unsigned int> (m_CurrentTimeStamp - elapsedTime);
//        不能大于  DELAY_THRESHOLD (100) ms
        sleepTime = sleepTime > DELAY_THRESHOLD ? DELAY_THRESHOLD : sleepTime;
        av_usleep(sleepTime);
    }
    delay = elapsedTime - m_CurrentTimeStamp;

    // TODO 随后再补充
    return delay;
}

int DecoderBase::DecodeOnePacket() {
    if (m_SeekPosition > 0) {
        auto seek_target = static_cast<int64_t >(m_SeekPosition * 1000000);
        int64_t seek_min = INT64_MIN;
        int64_t seek_max = INT64_MAX;
        int seek_ret = avformat_seek_file(m_AVFormatContext, -1, seek_min,
                                          seek_target, seek_max, 0);
        if (seek_ret < 0) {
            m_SeekSuccess = false;
        } else {
            if (m_StreamIndex != -1) {
                avcodec_flush_buffers(m_AVCodecContext);
            }
            ClearCache();
            m_SeekSuccess = true;
        }
        LOG_E("DecoderBase::DecodeOnePacket() seek_ret: %d", seek_ret);
    }
    int result = av_read_frame(m_AVFormatContext, m_AVPacket);
    while (result == 0) {
        if (m_AVPacket->stream_index == m_StreamIndex) {
            if (avcodec_send_packet(m_AVCodecContext, m_AVPacket) == AVERROR_EOF) {
                result = -1;
                LOG_E("DecoderBase::DecodeOnePacket  avcodec_send_packet AVERROR_EOF");
                goto __EXIT;
            }
            int frameCount = 0;
            while (avcodec_receive_frame(m_AVCodecContext, m_AVFrame)) {
                UpdateTimeStamp();
                AVSync();
                OnFrameAvailable(m_AVFrame);
                frameCount++;
            }
            if (frameCount > 0) {
                result = 0;
                LOG_E("DecoderBase::DecodeOnePacket  frameCount > 0");
                goto __EXIT;
            }
        }
        av_packet_unref(m_AVPacket);
        result = av_read_frame(m_AVFormatContext, m_AVPacket);

    }
    LOG_E("DecoderBase::DecodeOnePacket() result: %d", result);
    __EXIT:
    av_packet_unref(m_AVPacket);
    return result;
}

void DecoderBase::DoAVDecoding(DecoderBase *decoder) {
    do {
        if (decoder->InitFFDecoder() != 0) {
            LOG_E("DecoderBase::DoAVDecoding  InitFFDecoder  is no success");
            break;
        }
        decoder->OnDecoderReady();
        decoder->DecodingLoop();
    } while (false);
    decoder->UnInitFFDecoder();
    decoder->OnDecoderDone();
}

int DecoderBase::Init(const char *url, AVMediaType mediaType) {
    strcpy(m_Url, url);
    m_MediaType = mediaType;
    return 0;
}

void DecoderBase::UnInit() {
    if (m_Thread) {
        Stop();
        m_Thread->join();
        delete m_Thread;
        m_Thread = nullptr;
    }
}

AVCodecContext *DecoderBase::GetCodecContext() {
    return m_AVCodecContext;
}

void DecoderBase::SeekToPosition(float position) {

}

void DecoderBase::ClearCache() {}

void DecoderBase::SetMessageCallback(void *context, MessageCallback messageCallback) {
    this->m_MessageCallback = messageCallback;
    this->m_MsgContext = context;
}

void DecoderBase::SetAVSyncCallback(void *context, AVSyncCallback avSyncCallback) {
    m_MsgContext = context;
    m_AVSyncCallback = avSyncCallback;
}


